"""
Redis 工具：用于存储和校验登录状态/令牌
"""
from typing import Optional, Dict, Any
import json
from datetime import datetime

from utils.config import config

try:
    # redis-py 4.x 提供 asyncio 客户端
    from redis.asyncio import Redis
except Exception as e:  # pragma: no cover - 环境缺少依赖时给出友好错误
    Redis = None  # type: ignore


class RedisUtils:
    _client: Optional["Redis"] = None

    @classmethod
    def get_client(cls) -> "Redis":
        if Redis is None:
            raise RuntimeError(
                "redis-py[async] 未安装，请安装依赖：pip install 'redis>=4.5'"
            )
        if cls._client is None:
            cls._client = Redis(
                host=config.REDIS_HOST,
                port=config.REDIS_PORT,
                db=config.REDIS_DB,
                password=config.REDIS_PASSWORD,
                decode_responses=True,
            )
        return cls._client

    @staticmethod
    def _token_key(token: str) -> str:
        return f"{config.REDIS_KEY_PREFIX}token:{token}"

    @staticmethod
    def _user_tokens_key(user_id: int) -> str:
        return f"{config.REDIS_KEY_PREFIX}user:{user_id}:tokens"

    @classmethod
    async def save_token(
        cls,
        token: str,
        user_id: int,
        payload: Optional[Dict[str, Any]] = None,
        ttl_seconds: Optional[int] = None,
    ) -> None:
        client = cls.get_client()
        key = cls._token_key(token)
        value = {
            "user_id": user_id,
            "issued_at": datetime.utcnow().isoformat(),
        }
        if payload:
            value.update(payload)
        ttl = ttl_seconds or config.REDIS_TOKEN_TTL_SECONDS
        # 使用 set + 过期时间
        await client.set(key, json.dumps(value), ex=ttl)
        # 记录到用户 token 集合，便于日后管理（比如一键下线）
        await client.sadd(cls._user_tokens_key(user_id), token)
        await client.expire(cls._user_tokens_key(user_id), ttl)

    @classmethod
    async def get_token(cls, token: str) -> Optional[Dict[str, Any]]:
        client = cls.get_client()
        data = await client.get(cls._token_key(token))
        if not data:
            return None
        try:
            return json.loads(data)
        except Exception:
            return None

    @classmethod
    async def delete_token(cls, token: str) -> None:
        client = cls.get_client()
        # 尝试读取获得 user_id，以便从集合中移除
        info = await cls.get_token(token)
        await client.delete(cls._token_key(token))
        if info and "user_id" in info:
            await client.srem(cls._user_tokens_key(int(info["user_id"])), token)

    @classmethod
    async def delete_all_user_tokens(cls, user_id: int) -> int:
        """强制该用户所有会话下线，返回删除数量"""
        client = cls.get_client()
        key = cls._user_tokens_key(user_id)
        tokens = await client.smembers(key)
        deleted = 0
        for t in tokens:
            await client.delete(cls._token_key(t))
            deleted += 1
        await client.delete(key)
        return deleted


# 同步 Redis 客户端（用于队列操作等）
try:
    import redis
    _sync_client = None

    def get_redis():
        """获取同步 Redis 客户端（用于队列、缓存等非异步操作）"""
        global _sync_client
        if _sync_client is None:
            _sync_client = redis.Redis(
                host=config.REDIS_HOST,
                port=config.REDIS_PORT,
                db=config.REDIS_DB,
                password=config.REDIS_PASSWORD,
                decode_responses=True
            )
        return _sync_client
except ImportError:
    def get_redis():
        raise RuntimeError("redis 未安装，请安装依赖：pip install redis")

